/*
    KDE Plugin for 3D Office Environment
    Copyright (C) 2011  Till Merker

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "OfficeEnvKDEPlugin.h"

#include <kwinglutils.h>
#include <kactioncollection.h>
#include <kaction.h> 
#include <kdebug.h>
#include <assert.h>
#include <math.h>

#include "officeenv/OfficeEnv.h"

namespace KWin
{

#define IA_FORWARD 1
#define IA_BACKWARD 2
#define IA_TURN 4
#define IA_STEPLEFT 8
#define IA_STEPRIGHT 16
#define IA_UPDOWN 32

KWIN_EFFECT( officeenv, OfficeEnvKDEPlugin )
KWIN_EFFECT_SUPPORTED( officeenv, OfficeEnvKDEPlugin::supported() )

OfficeEnvKDEPlugin::OfficeEnvKDEPlugin() : Effect(), mActive(false), mSwitchingToDesktop(0), mActiveKeyAction(0), mSensitivity(2.0f),mOfficeEnv(NULL) {
    KActionCollection* actionCollection = new KActionCollection( this );
    KAction* a = ( KAction* )actionCollection->addAction( "ShowOfficeEnv" );
    a->setText( i18n( "Activate 3D Office Environment" ));
    a->setGlobalShortcut( KShortcut( Qt::ALT + Qt::Key_Escape ));
    mShortcut = a->globalShortcut();         
    connect( a, SIGNAL( triggered( bool )), this, SLOT( toggleActive()));
    connect( a, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( globalShortcutChanged(QKeySequence)));
}

OfficeEnvKDEPlugin::~OfficeEnvKDEPlugin() {
    if(mOfficeEnv) {
	mOfficeEnv->shutdown();
	delete mOfficeEnv;    
	for(int i = 0; i < effects->numberOfDesktops(); i++) delete mTexture[i];
	for(int i = 0; i < effects->numberOfDesktops(); i++) delete mRenderTarget[i];
	delete mTargetTexture;
	effects->addRepaintFull();
    }
}

bool OfficeEnvKDEPlugin::supported() {
    return GLRenderTarget::supported() && GLTexture::NPOTTextureSupported() &&
            (effects->compositingType() == OpenGLCompositing);
}

void OfficeEnvKDEPlugin::prePaintScreen( ScreenPrePaintData& data, int time ) {
    if(mValid && mActive) {
        data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST;
    }
    effects->prePaintScreen(data, time);
}
    
void OfficeEnvKDEPlugin::paintScreen( int mask, QRegion region, ScreenPaintData& data ) {
    if(mValid && mActive) {	    
	effects->paintScreen( mask, region, data );

	if (mActiveKeyAction) {
	    int forward = 0;
	    int sidestep = 0;
	    forward += mActiveKeyAction & IA_FORWARD ? 1 : 0;
	    forward -= mActiveKeyAction & IA_BACKWARD ? 1 : 0;
	    sidestep += mActiveKeyAction & IA_STEPRIGHT ? 1 : 0;
	    sidestep -= mActiveKeyAction & IA_STEPLEFT ? 1 : 0;
	    mOfficeEnv->inputAction(forward, sidestep, 0, 0);
	    effects->addRepaintFull();    
	}
	
	mOfficeEnv->renderFrame();

	/* Render the target texture on the screen */
	mTargetTexture->bind();
	glBegin(GL_QUADS);
	    glTexCoord2f(0.0, 1.0); glVertex2f(0.0, displayHeight());
	    glTexCoord2f(1.0, 1.0); glVertex2f(displayWidth(), displayHeight());
	    glTexCoord2f(1.0, 0.0); glVertex2f(displayWidth(), 0.0);
	    glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0);
	glEnd();
	mTargetTexture->unbind();	
	
	if(mOfficeEnv->getCurrentAnimation() != OfficeEnv::CA_None) effects->addRepaintFull();
	
    } else effects->paintScreen( mask, region, data );
}

void OfficeEnvKDEPlugin::postPaintScreen()
{
    effects->postPaintScreen();
    if(mActive && mSwitchingToDesktop && (mOfficeEnv->getCurrentAnimation() == OfficeEnv::CA_None)) {
	effects->setCurrentDesktop(mSwitchingToDesktop);
	mSwitchingToDesktop = 0;
	toggleActive();
    }
}

    
void OfficeEnvKDEPlugin::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) {
    if(mValid && mActive) w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); 
    effects->prePaintWindow( w, data, time );     
}
    
void OfficeEnvKDEPlugin::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) {
    if(mValid && mActive) 
    {
	/* Paint the window to it's desktop's target texture */
	if (w->desktop() >= 1 && w->desktop() <= effects->numberOfDesktops()) {
	    effects->pushRenderTarget(mRenderTarget[w->desktop()-1]);
	    effects->paintWindow(w, mask, region, data);
	    effects->popRenderTarget();
	} else {
	    /* Goes to all desktops */
	    for(int i = 0; i < effects->numberOfDesktops(); i++) {
		effects->pushRenderTarget(mRenderTarget[i]);
		effects->paintWindow(w, mask, region, data);
		effects->popRenderTarget();
	    }
	}
		
    } else effects->paintWindow(w, mask, region, data);
}

void OfficeEnvKDEPlugin::grabbedKeyboardEvent( QKeyEvent *e ) {
    if( e->type() == QEvent::KeyPress) {
	if( mShortcut.contains( e->key() + e->modifiers() ) ) {
	    toggleActive();
	    return;
	}
	    
        switch( e->key()) {
	    case Qt::Key_W:
		mActiveKeyAction |= IA_FORWARD;
		effects->addRepaintFull();		
		break;
	    case Qt::Key_S:
		mActiveKeyAction |= IA_BACKWARD;	
		effects->addRepaintFull();		
		break;	    
	    case Qt::Key_A:
		mActiveKeyAction |= IA_STEPLEFT;
		effects->addRepaintFull();		
		break;
	    case Qt::Key_D:
		mActiveKeyAction |= IA_STEPRIGHT;	
		effects->addRepaintFull();		
		break;
	    }	    	    
    } else  if( e->type() == QEvent::KeyRelease ) {    
	switch( e->key()) {
	    case Qt::Key_W:
		mActiveKeyAction &= ~IA_FORWARD;
		effects->addRepaintFull();		
		break;
	    case Qt::Key_S:
		mActiveKeyAction &= ~IA_BACKWARD;
		effects->addRepaintFull();		
		break;
	    case Qt::Key_A:
		mActiveKeyAction &= ~IA_STEPLEFT;
		effects->addRepaintFull();		
		break;
	    case Qt::Key_D:
		mActiveKeyAction &= ~IA_STEPRIGHT;
		effects->addRepaintFull();		
		break;
	}
    }
} 
    
void OfficeEnvKDEPlugin::globalShortcutChanged( const QKeySequence& seq ) {
    mShortcut = KShortcut( seq );
}     
    
void OfficeEnvKDEPlugin::toggleActive() {
    mActive = !mActive;

    if(mActive) {
	
	if(!mOfficeEnv) {
	    /* Initialize OfficeEnv */
	    mOfficeEnv = new OfficeEnv::OfficeEnv();
	    KConfigGroup conf = EffectsHandler::effectConfig( "OfficeEnv" );
	    QString office = conf.readEntry("SelectedOffice", "");
	    mOfficeEnv->startup(displayWidth(), displayHeight(), effects->numberOfDesktops(), office.toAscii().data());

	    mTargetTexture = new GLTexture(displayWidth(), displayHeight());
	    mTargetTexture->setTexture(mOfficeEnv->getTargetTexture());

	    mValid = true;
	    for (int i = 0; i < effects->numberOfDesktops(); i++ )
	    {
		mTexture[i] = new GLTexture(displayWidth(), displayHeight());
		mTexture[i]->setTexture(mOfficeEnv->getTexture(i));		    
		mRenderTarget[i] = new GLRenderTarget(mTexture[i]);
		mValid &= mRenderTarget[i]->valid();
	    }
	}
	
	/* Grab keyboard input, start mouse polling */
        mInputWindow = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), Qt::BlankCursor );
	XWarpPointer(display(), None, rootWindow(), 0, 0, 0, 0, displayWidth() / 2, displayHeight() / 2);	
	effects->startMousePolling();
        mKeyboardGrab = effects->grabKeyboard( this );
	effects->setActiveFullScreenEffect( this );
	effects->addRepaintFull();
	animationTime(1000.0f / 60.0f);
	mSwitchingToDesktop = 0;	
	mOfficeEnv->startAnimation(OfficeEnv::CA_ZoomOut, effects->currentDesktop() - 1);
	    
    } else  {
	
	effects->destroyInputWindow( mInputWindow );
	effects->stopMousePolling();
	if( mKeyboardGrab ) {
	    effects->ungrabKeyboard();
	    mKeyboardGrab = false;
	}                

	effects->setActiveFullScreenEffect( 0 );;
	effects->addRepaintFull();

    }
    
}
    
void OfficeEnvKDEPlugin::mouseChanged( const QPoint& pos, const QPoint& /*oldpos*/, Qt::MouseButtons buttons, 
            Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers, Qt::KeyboardModifiers )
{    
    if( mActive ) {
	QPoint middle(displayWidth() / 2, displayHeight() / 2);
	int deltaY = pos.y() - middle.y();
	int deltaX = pos.x() - middle.x();
	XWarpPointer(display(), None, rootWindow(), 0, 0, 0, 0, middle.x(), middle.y());
	 
	mOfficeEnv->inputAction(0, 0, (float)deltaX / (float)displayWidth() * mSensitivity, (float)deltaY / (float)displayHeight() * mSensitivity);
	
	if(!(buttons & Qt::LeftButton) && (oldbuttons & Qt::LeftButton)) {
	    if(mOfficeEnv->getTargetedDesktop() >= 0) {
		mSwitchingToDesktop = mOfficeEnv->getTargetedDesktop() + 1; 
		mOfficeEnv->startAnimation(OfficeEnv::CA_Switch, mOfficeEnv->getTargetedDesktop());
		effects->addRepaintFull();
	    }
	}
	effects->addRepaintFull();
    }
}
    
void OfficeEnvKDEPlugin::reconfigure( ReconfigureFlags flags ) {
    /* Configuration changed - shut down current office. It will be set up with the new values on activation. */
    if(mOfficeEnv) {	
	mOfficeEnv->shutdown();
	delete mOfficeEnv;
	mOfficeEnv = NULL;
	for(int i = 0; i < effects->numberOfDesktops(); i++) delete mTexture[i];
	for(int i = 0; i < effects->numberOfDesktops(); i++) delete mRenderTarget[i];
	delete mTargetTexture;
	effects->addRepaintFull();    
    }
}

} // namespace

